#region Using statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Rectangular.Model.Entities;
using Rectangular.Model.Environment;
using Rectangular.Util;
#endregion

namespace Rectangular.Controller
{
    public class RectangularGame : Microsoft.Xna.Framework.Game
    {
        #region Members
        /* The graphics device */
        GraphicsDeviceManager _graphics;
        
        /* Graphics are drawn in batches */
        SpriteBatch _spriteBatch;

        /* The font used in the game */
        private SpriteFont _font;

        /* The player */
        private Player _player;
        
        /* Monster */
        private List<Monster> _monsters;
        
        /* Bullets */
        public List<Bullet> _bullets;
        
        /* The map */
        private TileMap _tileMap;
        
        /* Input class */
        private PlayerInput _playerInput;
        #endregion

        #region Constructors
        /// <summary>
        /// Constructs a new game
        /// </summary>
        public RectangularGame()
        {
            // Create graphics device manager
            _graphics = new GraphicsDeviceManager(this);

            // Set content root directory
            Content.RootDirectory = "Content";
        }
        #endregion

        #region Methods
        /// <summary>
        /// Initializes various game objects
        /// </summary>
        protected override void Initialize()
        {
            // Set window width and height
            _graphics.PreferredBackBufferHeight = Camera.ScreenHeight;
            _graphics.PreferredBackBufferWidth = Camera.ScreenWidth;
            _graphics.ApplyChanges();

            // Show mouse visible
            this.IsMouseVisible = true;

            // Create new tileMap
            _tileMap = new TileMap(25, 25);
            _tileMap.LoadMap(Level.getLevel1());

            // Create player
            _player = new Player();
            _player.Position = new Vector2(400, 400);
            _player.Fired += PlayerFiredHandler;

            // Create monsters
            _monsters = new List<Monster>();
            for (int i = 0; i < 25; i++)
            {
                Monster monster = new Monster();
                monster.Position = VectorHelper.GenerateVector(800, 800);
                monster.Destination = VectorHelper.GenerateVector(800, 800);
                _monsters.Add(monster);
            }

            // Create a list of bullets
            _bullets = new List<Bullet>();

            // Initialize input class
            _playerInput = new PlayerInput(_player, _tileMap);

            base.Initialize();
        }

        /// <summary>
        /// Load content
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load tile texture
            Tile.Texture = Content.Load<Texture2D>(@"Textures\Grid");

            // Load sprite texture
            Sprite.Texture = Content.Load<Texture2D>(@"Textures\Sprite");

            // Load player texture
            Player.Texture = Content.Load<Texture2D>(@"Textures\Player");

            // Load monster texture
            Monster.Texture = Content.Load<Texture2D>(@"Textures\Player");

            // Load bullet texture
            Bullet.Texture = Content.Load<Texture2D>(@"Textures\Circle");

            // Load font
            _font = Content.Load<SpriteFont>(@"Fonts\Pesca");
        }

        /// <summary>
        /// Unloads any non content manager content
        /// </summary>
        protected override void UnloadContent()
        {
        }

        /// <summary>
        /// Updates the various game objects
        /// </summary>
        /// <param name="gameTime"></param>
        protected override void Update(GameTime gameTime)
        {       
            // Handles keyboard input
            _player.Update(gameTime);
            _playerInput.Update(gameTime);

            // Update bullets
            UpdateBullets(gameTime);

            // Update monsters
            UpdateMonsters(gameTime);
            
            // Detect collisions
            CollisionManager.ProcessBulletsVsMonsters(_bullets, _monsters);

            base.Update(gameTime);
        }

        /// <summary>
        /// Updates bullet positions
        /// </summary>
        /// <param name="gameTime"></param>
        public void UpdateBullets(GameTime gameTime)
        {
            for (int i = 0; i < _bullets.Count; i++)
            {
                // Update bullet
                Bullet bullet = _bullets[i];
                bullet.Update(gameTime);

                // If the bullet is of screen boundaries delete it
                if (bullet.Position.X < Camera.Position.X || bullet.Position.X > Camera.ScreenWidth ||
                    bullet.Position.Y < Camera.Position.Y || bullet.Position.Y > Camera.ScreenHeight)
                {
                    _bullets.Remove(bullet);
                }
            }
        }

        /// <summary>
        /// Updates monster positions
        /// </summary>
        /// <param name="gameTime"></param>
        public void UpdateMonsters(GameTime gameTime)
        {
            for (int i = 0; i < _monsters.Count; i++)
            {
                // Update monster position
                _monsters[i].Update(gameTime);

                // If a monster has reached its destination set a new destination
                if (_monsters[i].AtDestination())
                {
                    _monsters[i].Destination = VectorHelper.GenerateVector(800, 800);
                }
            }
        }

        /// <summary>
        /// Draws the various game objects
        /// </summary>
        /// <param name="gameTime"></param>
        protected override void Draw(GameTime gameTime)
        {
            // Clear graphics device
            GraphicsDevice.Clear(Color.Black);

            // Start drawing
            _spriteBatch.Begin();

            // Draw tile map
            _tileMap.Draw(_spriteBatch);

            // Draw player
            _player.Draw(_spriteBatch);

            // Draw monsters
            foreach (Monster monster in _monsters)
            {
                monster.Draw(_spriteBatch);
            }

            // Draw bullets
            foreach (Bullet bullet in _bullets)
            {
                bullet.Draw(_spriteBatch);
            }

            // Debug

            _spriteBatch.DrawString(_font, "Bullets: " + _bullets.Count, new Vector2(100, 500), Color.White);

            _spriteBatch.DrawString(_font, "Cam X:" + Camera.Position.X, new Vector2(100, 600), Color.White);
            _spriteBatch.DrawString(_font, "Cam Y:" + Camera.Position.Y, new Vector2(100, 620), Color.White);
            _spriteBatch.DrawString(_font, "Cam Width:" + Camera.ScreenWidth, new Vector2(100, 640), Color.White);
            _spriteBatch.DrawString(_font, "Cam Width:" + Camera.ScreenWidth / 4, new Vector2(100, 660), Color.White);

            _spriteBatch.DrawString(_font, "Pl X:" + _player.Position.X, new Vector2(100, 700), Color.White);
            _spriteBatch.DrawString(_font, "Pl Y:" + _player.Position.Y, new Vector2(100, 720), Color.White);

            // End drawing
            _spriteBatch.End();

            // Draw base
            base.Draw(gameTime);
        }
        #endregion

        #region Event handlers
        private void PlayerFiredHandler(object sender, FiredEventArgs args)
        {
            // Adds a bullet to the game
            _bullets.Add(new Bullet()
            {
                Position = _player.Position,
                Destination = args.Destination
            });
        }
        #endregion
    }
}